use crate::{common::*, pipeline::{ImmutExecutor, MutExecutor}};
use super::*;

pub struct ChunkHasher {
    hash_time:u128,
    hasher:FingerPrintConfig,
}

impl ChunkHasher {
    pub fn from_source(source:&SourcePool) -> Self {
        let hash_builder:FingerPrintBuilder = source.get_config().get("fingerprint").unwrap();
        Self { hash_time:0, hasher:hash_builder.build() }
    }

    pub fn hash_chunk(&self, chunk:&mut Chunk) {
        chunk.hash(self.hasher);
    }
}

impl ImmutExecutor<WriteItem, WriteItem> for ChunkHasher {
    type Error = BackupError;
    fn exec<'scope, F:FnMut(WriteItem)>(&'scope self, input:WriteItem, mut output:F) -> Result<(), Self::Error> {
        if let WriteItem::Chunk(mut seq_chunk) = input {
            self.hash_chunk(&mut seq_chunk.chunk);
            output(WriteItem::Chunk(seq_chunk));
        } else {
            output(input);
        }
        Ok(())
    }
}

impl MutExecutor<WriteItem, WriteItem> for ChunkHasher {
    type Error = BackupError;
    fn exec<'scope, F:FnMut(WriteItem)>(&'scope mut self, input:WriteItem, mut output:F) -> Result<(), Self::Error> {
        if let WriteItem::Chunk(mut seq_chunk) = input {
            let now = std::time::Instant::now();
            self.hash_chunk(&mut seq_chunk.chunk);
            self.hash_time += now.elapsed().as_micros();
            output(WriteItem::Chunk(seq_chunk));
        } else {
            output(input);
        }
        Ok(())
    }
}

impl Evaluated for ChunkHasher {
    fn register(&self, evaluation:&Evaluation) {
        let mut eva = evaluation.lock();
        eva.register(BackupEvaluation::Time, "hash time(us)");
    }

    fn set_value(&self, evaluation:&Evaluation) {
        let mut eva = evaluation.lock();
        eva.set("hash time(us)", self.hash_time);
    }
}